/***************************************************************************
 *   This is a part of the NextCAD Web APP.                                *
 *   Copyright (C) AMCAX, All rights reserved.                             *
 ***************************************************************************/

import { pickedFaceData, pickedLineData } from '@amcax/base';
import { MessageTip } from '@app-base/common/utils/MessageTip';
import {
  NumberInputFormat,
  PickInputRJSFFormat,
} from '@app-cad/common/components/command-form/constants';
import { NonDialogPickableCommandForm } from '@app-cad/common/components/command-form/pickable';
import {
  MULTI_SOLUTION_SCHEMA,
  MultiSolutionWidget,
} from '@app-cad/common/components/command-form/widgets/multi-solution';
import { useCommand } from '@app-cad/common/hooks/useCommand';
import { useTools } from '@app-cad/common/hooks/useTools';
import {
  getDefaultLawCurveValue,
  getLawCurveFromJSONString,
  isLawCurveValid,
  LawCurve,
  LawCurveButton,
} from '@app-cad/common/law-curve';
import { getIdOfPickedObj } from '@app-cad/common/utils/render';
import { RJSFSchema } from '@rjsf/utils';
import { useAsyncEffect } from 'ahooks';
import { omitBy } from 'lodash';
import { useEffect, useMemo, useState } from 'react';
import { ChildLinearSweepCommandProps } from './types';

type lengthType = 'FromCurve' | 'Standard' | 'FromTo' | 'FromExtremum';

type FormData = {
  guide?: pickedLineData;
  direction?: pickedLineData | pickedFaceData;
  jsonAngle: string;
  length1Type: string;
  length2Type: string;
  length1Value?: number;
  length2Value?: number;
  length1Surface?: pickedFaceData;
  length2Surface?: pickedFaceData;
};

type lengthParams = {
  type: lengthType;
  value?: number;
  surface?: string;
};

type Params = {
  mode: 'DraftDirection';
  guide: string;
  direction: string;
  lineLawAngle: LawCurve;
  length1: lengthParams;
  length2: lengthParams;
  solution: number;
};

export function Draft({
  onClose,
  editDxid,
  props,
}: ChildLinearSweepCommandProps) {
  const initData: FormData = {
    jsonAngle: getDefaultLawCurveValue(),
    length1Type: 'Standard',
    length2Type: 'Standard',
    length1Value: 20,
    length2Value: 0,
  };

  const [solutionNum, setSolutionNum] = useState(0);
  const [solution, setSolution] = useState(0);
  const [formData, setFormData] = useState<FormData>(initData);

  const getLengthTypeRJSFSchema = (title: string) => ({
    title,
    type: 'string',
    oneOf: [
      {
        type: 'string',
        title: '标准',
        const: 'Standard',
      },
      {
        type: 'string',
        title: '从曲线',
        const: 'FromCurve',
      },
      {
        type: 'string',
        title: '从/到',
        const: 'FromTo',
      },
      {
        type: 'string',
        title: '从极值',
        const: 'FromExtremum',
      },
    ],
    default: 'Standard',
  });

  const getLengthRJSFSchema = ({
    typeKey,
    typeValue,
    valueKey,
    surfaceKey,
    title,
  }: {
    typeKey: string;
    typeValue: string;
    valueKey: string;
    surfaceKey: string;
    title: string;
  }) => {
    const schema = omitBy(
      {
        [typeKey]: getLengthTypeRJSFSchema(title + '类型'),
        [valueKey]:
          typeValue === 'Standard' || typeValue === 'FromExtremum'
            ? {
                title: title,
                type: 'number',
                format: NumberInputFormat.Length,
              }
            : null,
        [surfaceKey]:
          typeValue === 'FromTo'
            ? {
                type: 'string',
                format: PickInputRJSFFormat.PickSurface,
              }
            : null,
      },
      (v) => v == null,
    );

    const uiSchema = {
      [valueKey]: {
        'ui:placeholder': '请输入值',
      },
      [surfaceKey]: {
        'ui:placeholder': '选择曲面',
      },
    };

    return {
      schema,
      uiSchema,
    };
  };

  const length1Properties = getLengthRJSFSchema({
    title: '长度1',
    typeKey: 'length1Type',
    valueKey: 'length1Value',
    surfaceKey: 'length1Surface',
    typeValue: formData.length1Type,
  });

  const length2Properties = getLengthRJSFSchema({
    title: '长度2',
    typeKey: 'length2Type',
    valueKey: 'length2Value',
    surfaceKey: 'length2Surface',
    typeValue: formData.length2Type,
  });

  const schema: RJSFSchema = {
    type: 'object',
    properties: {
      guide: {
        type: 'string',
        format: PickInputRJSFFormat.PickCurve,
      },
      direction: {
        type: 'string',
        format: PickInputRJSFFormat.PickDirection,
      },
      jsonAngle: {
        title: '角度',
        type: 'string',
      },
      ...length1Properties.schema,
      ...length2Properties.schema,
      solution: MULTI_SOLUTION_SCHEMA,
    },
  };

  const { picker } = useTools();

  useAsyncEffect(async () => {
    if (props == null) {
      setFormData(initData);
      return;
    }
    const {
      guide,
      direction,
      lineLawAngle,
      length1,
      length2,
      solution: sol,
    }: Params = props.params;
    await initEditDependency(
      [guide, direction, lineLawAngle?.bsplineId].filter((id) => id != null),
    );
    setFormData({
      guide: picker.generatePickObjByPickID(guide) as pickedLineData,
      direction: picker.generatePickObjByPickID(direction) as pickedLineData,
      jsonAngle: JSON.stringify(lineLawAngle),
      length1Type: length1?.type,
      length2Type: length2?.type,
      length1Value: length1?.value,
      length2Value: length2?.value,
      length1Surface: picker.generatePickObjByPickID(
        length1?.surface,
      ) as pickedFaceData,
      length2Surface: picker.generatePickObjByPickID(
        length2?.surface,
      ) as pickedFaceData,
    });
    setSolution(sol);
  }, [editDxid]);

  const islengthFormDataValid = (type, value, surface) => {
    if (type === 'Standard' || type === 'FromExtremum') {
      return value != null;
    }
    if (type === 'FromTo') {
      return surface != null;
    }
    return true;
  };

  const isFormDataValid = () => {
    const {
      length1Type,
      length1Value,
      length1Surface,
      length2Type,
      length2Value,
      length2Surface,
      guide,
      direction,
      jsonAngle,
    } = formData;
    const basicValid = guide != null && direction != null;
    const typeValid = !(
      length1Type === length2Type && length1Type === 'FromCurve'
    );
    const angleValid = isLawCurveValid(jsonAngle);
    const length1Vaild = islengthFormDataValid(
      length1Type,
      length1Value,
      length1Surface,
    );
    const length2Vaild = islengthFormDataValid(
      length2Type,
      length2Value,
      length2Surface,
    );
    return (
      basicValid && angleValid && length1Vaild && length2Vaild && typeValid
    );
  };

  const checkAndFinishDraw = async () => {
    const { length1Value, length2Value } = formData;
    if (!isFormDataValid()) {
      return;
    }
    if (length1Value === length2Value && length1Value === 0) {
      MessageTip.error('长度参数不能同时为0');
      return;
    }
    await execute(false);
    onClose();
  };

  const { initEditDependency, initingEdit, clear, confirmCommand, isLoading } =
    useCommand('cmd-linear-two-limits-curve', {
      commandName: 'CreateSurface',
      editDxid,
      onClose,
      finishDraw: checkAndFinishDraw,
    });

  const getLengthParamsFromFormData = (type, value, surface) => {
    return {
      type,
      value: ['Standard', 'FormExtremum'].includes(type) ? value : null,
      surface: type === 'FromTo' ? getIdOfPickedObj(surface) : null,
    };
  };

  const execute = (preview: boolean) => {
    const {
      guide,
      direction,
      length1Type,
      length1Value,
      length1Surface,
      length2Type,
      length2Value,
      length2Surface,
      jsonAngle,
    } = formData;
    return confirmCommand(
      {
        type: 'LinearSweep',
        a: {
          mode: 'DraftDirection',
          guide: getIdOfPickedObj(guide),
          direction: getIdOfPickedObj(direction),
          lineLawAngle: getLawCurveFromJSONString(jsonAngle),
          length1: getLengthParamsFromFormData(
            length1Type,
            length1Value,
            length1Surface,
          ),
          length2: getLengthParamsFromFormData(
            length2Type,
            length2Value,
            length2Surface,
          ),
          solution,
        },
      },
      preview,
    );
  };

  const preview = async () => {
    const { length1Value, length2Value } = formData;
    if (!isFormDataValid()) {
      clear();
      setSolution(0);
      setSolutionNum(0);
      return;
    }
    if (length1Value === length2Value && length1Value === 0) {
      MessageTip.error('长度参数不能同时为0');
      clear();
      setSolution(0);
      setSolutionNum(0);
      return;
    }

    try {
      const { nsolution } = await execute(true);
      setSolutionNum(nsolution);
    } catch {
      // Ignore errors during preview
      clear();
      setSolution(0);
      setSolutionNum(0);
    }
  };

  const finalFormData = useMemo(
    () => ({
      ...formData,
      solution: [solution, solutionNum],
    }),
    [formData, solution, solutionNum],
  );

  useEffect(() => {
    preview();
  }, [formData, solution]);

  return (
    <NonDialogPickableCommandForm
      disabled={initingEdit}
      onCancel={onClose}
      schema={schema}
      submitLoading={isLoading}
      formData={finalFormData}
      onChange={(v) => {
        setFormData(v);
        setSolution(v.solution[0]);
      }}
      submitText='完成'
      onSubmit={checkAndFinishDraw}
      extraUiSchema={{
        guide: {
          'ui:autofocus': true,
          'ui:placeholder': '请输入引导线',
        },
        direction: {
          'ui:placeholder': '请输入方向',
        },
        jsonAngle: {
          'ui:widget': LawCurveButton,
          'ui:options': {
            suffix: '度',
          },
        },
        ...length1Properties.uiSchema,
        ...length2Properties.uiSchema,
        solution: {
          'ui:widget': MultiSolutionWidget,
        },
      }}
      isFormDataValid={isFormDataValid()}
    />
  );
}
